package Dungeoneering.Events;

import java.util.Comparator;
import java.util.PriorityQueue;

import Dungeoneering.Utils.Utilities;


/**
 * The EventCaster will keep a PriorityQueue of Dungeoneering Tasks which will be run by order of priority
 * @author Nicolaas
 *
 */
public class EventCaster implements Runnable {
	
	private static final EventCaster INSTANCE = new EventCaster();
	
	private final PriorityQueue<DungeoneeringTask> queue;
	
	private DungeoneeringTask event;
	
	/**
	 * test code for the EventCaster
	 * @param args
	 */
	public static void main(String[] args) {
		new Thread(getInstance()).start();
		DungeoneeringTask dg = new DungeoneeringTask(7);
		getInstance().dispatchEvent(new DungeoneeringTask(8));
		getInstance().dispatchEvent(new DungeoneeringTask(5));
		getInstance().dispatchEvent(new DungeoneeringTask(6));
		Utilities.sleep(700);
		getInstance().dispatchEvent(dg);
		dg.changePriority(1);
		
		
	}
	
	private EventCaster() {
		queue = new PriorityQueue<DungeoneeringTask>(500, new Comparator<DungeoneeringTask>() {

			@Override
			public int compare(DungeoneeringTask d, DungeoneeringTask f) {
				if (d.getPriority() < f.getPriority())
					return 1;
				return -1;
			}
		});
	}
	
	public static EventCaster getInstance() {
		return INSTANCE;
	}
	
	/**
	 * Adds an event to the queue for processing.
	 * 
	 */
	public void dispatchEvent(DungeoneeringTask e) {
		synchronized (queue) {
			if (queue.contains(e)) {
				queue.remove(e);
				System.out.println("Removed an old Task. Re-adding");
			} else if (event != null && event.equals(e)) {
				if (queue.peek() != null && event.getPriority() >= queue.peek().getPriority()) {
					// no need to redispatch it as it's hot highest priority anyway.
					return;
				} else {
					event.interupt();
					event = null; // not needed but it's good practice :)
				}
			}
			if (event != null && e.getPriority() > event.getPriority()) {
				event.interupt();
				queue.add(event); // re-add for later processing
			}
			queue.add(e);
			queue.notifyAll();
		}
	}
	
	/**
	 * Dispatches the given event. Calling this avoids the use of the event
	 * queue.
	 */
	public void processEvent(final DungeoneeringTask event) {
		event.process();
	}

	@Override
	public void run() {
		while (true) {
			try {
				synchronized (queue) {
					while (queue.isEmpty()) {
						try {
							queue.wait();
						} catch (final Exception e) {
							System.out.println("Event Queue: " + e.toString());
						}
					}
					event = queue.remove();
				}
				try {
					processEvent(event);
				} catch (final Throwable e) {
					e.printStackTrace();
				}
			} catch (final Exception e) {
				System.err.println("Event Queue: " + e.toString());
				e.printStackTrace();
			}
		}
		
	}


}
